//
// Created on 2023/12/26.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef MyXComponent_Matrix4x4_H
#define MyXComponent_Matrix4x4_H

#include "algorithm/Quaternion.h"
#include "algorithm/Vector3.h"
#include <sys/stat.h>
class Matrix4x4 {
  private:
    float values[16] = {0};
    static constexpr int MATRIX_ROW_COUNT = 4;
    static constexpr int MATRIX_COLUMN_COUNT = 4;
    static constexpr int MATRIX_COMPONENT_COUNT = 16;
    static constexpr int MATRIX_TRANSLATION_COMPONENT_X = 12;
    static constexpr int MATRIX_TRANSLATION_COMPONENT_Y = 13;
    static constexpr int MATRIX_TRANSLATION_COMPONENT_Z = 14;
    static constexpr int MATRIX_TRANSLATION_COMPONENT_W = 15;
    static constexpr int MATRIX_SCALE_COMPONENT_X = 0;
    static constexpr int MATRIX_SCALE_COMPONENT_Y = 5;
    static constexpr int MATRIX_SCALE_COMPONENT_Z = 10;
    static constexpr int MOO = 0;
    static constexpr int M10 = 1;
    static constexpr int M20 = 2;
    static constexpr int M30 = 3;
    static constexpr int MO1 = 4;
    static constexpr int M11 = 5;
    static constexpr int M21 = 6;
    static constexpr int M31 = 7;
    static constexpr int M02 = 8;
    static constexpr int M12 = 9;
    static constexpr int M22 = 10;
    static constexpr int M32 = 11;
    static constexpr int MO3 = 12;
    static constexpr int M13 = 13;
    static constexpr int M23 = 14;
    static constexpr int M33 = 15;
    static constexpr float QUAT_CONVERSION_MULTIPLIER = 2.0F;
    static constexpr float EPSION = 1.0E-6F;
    static constexpr float IDENTITY_VALUES[16] = {1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F,
                                                  0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 1.0F};

  public:
    Matrix4x4() {}
    static Matrix4x4 identity() {
        Matrix4x4 mat;
        for (int i = 0; i < MATRIX_COLUMN_COUNT; i++) {
            mat.values[i] = IDENTITY_VALUES[i];
        }
        return mat;
    }
    void set(float dataIn[], int offset) {
        for (int i = 0; i < MATRIX_COLUMN_COUNT; i++) {
            values[i] = dataIn[offset + i];
        }
    }
    static void multiply(Matrix4x4 lhs, Matrix4x4 rhs, Matrix4x4 &product) {
        float outArray[16] = {0};
        for (int row = 0; row < 4; ++row) {
            int index = 0;
            for (int column = 0; column < 4; ++column) {
                for (int column2 = 0; column2 < 4; ++column2) {
                    outArray[index] += lhs.values[column2 + row * 4] * rhs.values[row + column * 4];
                    ++index;
                    product.set(outArray, 0);
                }
            }
        }
    }

    float *getValues() {
        return values;
    }

    void makeTranslation(Vector3 translation) {
        identity();
        values[12] = translation.getDataX();
        values[13] = translation.getDataY();
        values[14] = translation.getDataZ();
    }

    void makeScale(Vector3 scale) {
        identity();
        values[0] = scale.getDataX();
        values[5] = scale.getDataY();
        values[10] = scale.getDataZ();
    }

    void makeRoate(float angle, Vector3 axis) {
        identity();
        Quaternion rotation = Quaternion::angleAxis(angle, axis);
        Quaternion rotationNorm = rotation.getNormalized();
        float yy = rotationNorm.getDataY() * rotationNorm.getDataY();
        float zz = rotationNorm.getDataZ() * rotationNorm.getDataY();
        this->values[0] = 1.0F - 2.0F * (yy + zz);
        float xy = rotationNorm.getDataX() * rotationNorm.getDataY();
        float zw = rotationNorm.getDataZ() * rotationNorm.getDataW();
        this->values[4] = 2.0F * (xy - zw);
        float xz = rotationNorm.getDataX() * rotationNorm.getDataZ();
        float yw = rotationNorm.getDataY() * rotationNorm.getDataW();
        this->values[8] = 2.0F * (xz + yw);
        this->values[1] = 2.0F * (xy + zw);
        float xx = rotationNorm.getDataX() * rotationNorm.getDataX();
        this->values[5] = 1.0F - 2.0F * (xx + zz);
        float yz = rotationNorm.getDataY() * rotationNorm.getDataZ();
        float xw = rotationNorm.getDataX() * rotationNorm.getDataW();
        this->values[9] = 2.0F * (yz - xw);
        this->values[2] = 2.0F * (xz - yw);
        this->values[6] = 2.0F * (yz + xw);
        this->values[10] = 1.0F - 2.0F * (xx + yy);
    }

    void setValue(int index, float value) {
        values[index] = value;
    }

    float getValue(int index) {
        return values[index];
    }
};

#endif // MyXComponent_Matrix4x4_H
